package com.excilys.jnullsafe;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;

/**
 * Not
 */
public class ReturnType {

    private Type type;

    public ReturnType(Method method) {
        if (method == null) {
            throw new IllegalArgumentException("method should not be null");
        }
        type = method.getGenericReturnType();

    }

    public boolean isVoid() {
        return type.equals(void.class) || type.equals(Void.class);
    }

    public boolean isGeneric() {
        return type instanceof TypeVariable<?>;
    }

    public void extractTypeArgument(Type parameterizedType) {

        if (!isGeneric()) {
            throw new IllegalStateException("type should be a TypeVariable");
        }

        type = findCorrespondingTypeArgument((ParameterizedType) parameterizedType, (TypeVariable<?>) type);
    }

    public boolean isArray() {
        if (type instanceof Class<?>) {
            Class<?> typeClass = (Class<?>) type;
            return typeClass.isArray();
        } else {
            return false;
        }
    }

    public boolean isPrimitive() {
        if (type instanceof Class<?>) {
            Class<?> typeClass = (Class<?>) type;
            return typeClass.isPrimitive() && typeClass != void.class;
        } else {
            return false;
        }
    }

    public Type asType() {
        return type;
    }

    private static Type findCorrespondingTypeArgument(ParameterizedType parameterizedType, TypeVariable<?> typeVariable) {

        if (parameterizedType == null) {
            throw new IllegalArgumentException("parameterizedType should not be null");
        }

        TypeVariable<?>[] typeVariables = typeVariable.getGenericDeclaration().getTypeParameters();

        int i = 0;
        for (TypeVariable<?> typeVariableIt : typeVariables) {
            if (typeVariableIt.equals(typeVariable)) {
                break;
            }
            i++;
        }

        Type[] instanceParameters = parameterizedType.getActualTypeArguments();

        return instanceParameters[i];
    }

}
